package com.mind.customer.api.ocr;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.mind.common.utils.StringUtils;
import com.mind.common.utils.file.vo.FileVo;
import com.mind.customer.api.helper.FieldHelper;
import com.mind.customer.domain.vo.OCRData;
import com.mind.customer.domain.vo.TickData;
import com.mind.customer.exception.AjObjectNotFoundException;
import com.mind.customer.handler.common.CommonHandler;
import com.mind.customer.mapper.AjAttachTypeMapper;
import com.mind.customer.mapper.AjDatasourceMapper;
import com.mind.customer.mapper.AjObjectMapper;
import com.mind.customer.utils.CountUtils;
import com.mind.system.domain.*;
import com.mind.system.mapper.AttachResultMapper;
import com.mind.system.mapper.SysConfigMapper;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import okhttp3.*;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import com.mind.common.utils.file.FileUtils;

@Component
@Log4j2
@Data
public class OCRUtils {
    /**
     * 配置的登录URL
     */
    private String loginUrl;

    private String fanhanAIOCRURL;

    private String fanhanAIContractTableURL;

    private String model_name = "Qwen2-72B";

    private Integer contentTimeoutMinutes = 10;
    /**
     * 配置的登录名
     */
    private String username;
    /**
     * 配置的用户名密码
     */
    private String password;
    /**
     * 登录的token
     */
    private String token;

    @Resource
    private AjAttachTypeMapper attachTypeMapper;


    @Resource
    private FieldHelper fieldHelper;


    @Resource
    private SysConfigMapper sysConfigMapper;

    @Resource
    private CommonHandler commonHandler;

    @Resource
    private AjDatasourceMapper datasourceMapper;
    @Resource
    private AjObjectMapper ajObjectMapper;


    private String ndUrl;
    private String ndToken;
    private String ndParams;
    private String fanhanAIStampURL;
    private String ncUrl;
    private String ncSecret;
    private String ncScenarioId;
    private long ncSleep = 10;
    @Resource
    private CountUtils countUtils;
    @Autowired
    private AttachResultMapper attachResultMapper;

    /**
     * 两小时更新一次token
     * 2025 xp注释
     */
    //@Scheduled(fixedDelay = 1000 * 60 * 60 * 2)
    public void updateToken() {
        setToken();
        // 不需要ND
        //setNdToken();
    }


    private void initConfig() {
        SysConfig timeOutSysConfig = sysConfigMapper.checkConfigKeyUnique("ContentTimeoutMinutes");
        if (timeOutSysConfig != null) {
            try {
                contentTimeoutMinutes = Integer.parseInt(timeOutSysConfig.getConfigValue());
            } catch (Exception e) {
                log.error("ContentTimeoutMinutes配置错误,请检查配置");
            }
        }
        SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("FanHanAIOCRURL");
        if (sysConfig == null) {
            log.error("没有配置检查, FanHanAIOCRURL");
            return;
        }
        fanhanAIOCRURL = sysConfig.getConfigValue();
        SysConfig contentConfig = sysConfigMapper.checkConfigKeyUnique("FanhanAIContractTableURL");
        if (contentConfig == null) {
            log.error("没有配置获取, FanHanAIGetTicketContentUrl");
            return;
        }

        fanhanAIOCRURL = sysConfig.getConfigValue();
        SysConfig stampConfig = sysConfigMapper.checkConfigKeyUnique("FanhanAIStamp");
        if (stampConfig == null) {
            log.error("没有配置获取, FanhanAIStamp");
            return;
        }
        fanhanAIStampURL = stampConfig.getConfigValue();
        fanhanAIContractTableURL = contentConfig.getConfigValue();
        SysConfig usernameConfig = sysConfigMapper.checkConfigKeyUnique("FanHanAIOCRUsername");
        SysConfig passwordConfig = sysConfigMapper.checkConfigKeyUnique("FanHanAIOCRPassword");
        if (usernameConfig != null && StringUtils.isNotEmpty(usernameConfig.getConfigValue()) && passwordConfig != null && StringUtils.isNotEmpty(passwordConfig.getConfigValue())) {
            username = usernameConfig.getConfigValue();
            password = passwordConfig.getConfigValue();
        }
        SysConfig ndUrlConfig = sysConfigMapper.checkConfigKeyUnique("ND_URL");
        if (ndUrlConfig != null && StringUtils.isNotEmpty(ndUrlConfig.getConfigValue())) {
            ndUrl = ndUrlConfig.getConfigValue();
        }
        SysConfig ncUrlConfig = sysConfigMapper.checkConfigKeyUnique("NC_URL");
        if (ncUrlConfig != null && StringUtils.isNotEmpty(ncUrlConfig.getConfigValue())) {
            ncUrl = ncUrlConfig.getConfigValue();
        }else{
            ncUrl="http://nd.fanhaninfo.test";
        }
        SysConfig ncTokenConfig = sysConfigMapper.checkConfigKeyUnique("NC_TOKEN");
        if (ncTokenConfig != null && StringUtils.isNotEmpty(ncTokenConfig.getConfigValue())) {
            ncSecret = ncTokenConfig.getConfigValue();
        }else{
            ncSecret = "51c1bc9d-9d01-4e98-bcb4-0125bbb37cb7";
        }
        SysConfig ncScenarioIdConfig = sysConfigMapper.checkConfigKeyUnique("NC_SCENARIOID");
        if (ncScenarioIdConfig != null && StringUtils.isNotEmpty(ncScenarioIdConfig.getConfigValue())) {
            ncScenarioId = ncScenarioIdConfig.getConfigValue();
        }else{
            ncScenarioId = "8";
        }
        // nc请求睡眠时间
        SysConfig ncSleepConfig = sysConfigMapper.checkConfigKeyUnique("NC_SLEEP");
        if (ncSleepConfig != null && StringUtils.isNotEmpty(ncSleepConfig.getConfigValue())) {
            ncSleep = Long.parseLong(ncSleepConfig.getConfigValue());
        }
        SysConfig ndParamsConfig = sysConfigMapper.checkConfigKeyUnique("ND_LOGIN_PARAMS");
        if (ndParamsConfig != null && StringUtils.isNotEmpty(ndParamsConfig.getConfigValue())) {
            ndParams = ndParamsConfig.getConfigValue();
        }
    }

    /**
     * 获取OCRToken
     */
    private void setToken() {
        initConfig();
        if (StringUtils.isEmpty(fanhanAIOCRURL) || StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            log.info("配置不正确，无需加载token;fanhanAIOCRURL:{}, username: {}, password: {}", fanhanAIOCRURL, username, password);
            return;
        }
        URL url = null;
        try {
            url = new URL(fanhanAIOCRURL);
        } catch (MalformedURLException e) {
            log.error("fanHanAICheckTicketTypeUrl: {} 不是正确的URL", fanhanAIOCRURL, e);
            return;
        }

        String loginUrl = url.getProtocol() + "://" + url.getHost();
        if (url.getPort() != -1) {
            loginUrl += ":" + url.getPort();
        }
        loginUrl += "/jsonrpc";
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"),
                JSON.toJSONString(new HashMap<String, Object>() {{
                    put("method", "/account/login");
                    put("params", new HashMap<String, String>(2) {{
                                put("username", username);
                                put("password", password);
                            }}
                    );
                }}));
        // 创建请求
        Request request = new Request.Builder()
                .url(loginUrl)
                .post(body)
                .build();

        try (Response response = client.newCall(request).execute()) {
            // 检查响应码
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

            // 打印响应体
            String bodyStr = response.body().string();
            log.info("Get FanhanOCRLogin result >> {}", bodyStr);
            String accessToken = JSON.parseObject(bodyStr).getJSONObject("result").getString("token");
            this.token = accessToken;
            log.info("Get FanhanOCRLogin token >> {}", accessToken);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void setNdToken() {
        if (StringUtils.isEmpty(ndUrl) || StringUtils.isEmpty(ndParams)) {
            log.info("配置不正确，无需加载token;NDAIURL:{}, ndParams: {}", ndUrl, ndParams);
            return;
        }
        URL url = null;
        try {
            url = new URL(ndUrl);
        } catch (MalformedURLException e) {
            log.error("NDAIURL: {} 不是正确的URL", ndUrl, e);
            return;
        }

        String loginUrl = url.getProtocol() + "://" + url.getHost();
        if (url.getPort() != -1) {
            loginUrl += ":" + url.getPort();
        }
        loginUrl += "/jsonrpc";
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"),
                JSON.toJSONString(new HashMap<String, Object>() {{
                    put("method", "/account/login");
                    put("params", JSONObject.parseObject(ndParams)
                    );
                }}));
        // 创建请求
        Request request = new Request.Builder()
                .url(loginUrl)
                .post(body)
                .build();

        try (Response response = client.newCall(request).execute()) {
            // 检查响应码

            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

            // 打印响应体
            String bodyStr = response.body().string();
            log.info("Get NDLogin result url -> {} params -> {} >> {}", loginUrl,JSON.toJSONString(new HashMap<String, Object>() {{
                put("method", "/account/login");
                put("params", JSONObject.parseObject(ndParams)
                );
            }}), bodyStr);
            String accessToken = JSON.parseObject(bodyStr).getJSONObject("result").getString("token");
            this.ndToken = accessToken;
            log.info("Get NDLogin token >> {}", accessToken);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param fileVo
     * @return
     * @throws IOException
     */
    public List<TickData> checkReport(AjTicketTask task, FileVo fileVo, boolean isGetContent) throws IOException, AjObjectNotFoundException {
        // ND识别
//        SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("ND_CATE_INTERFACE_PARAMS");
//        if (sysConfig != null && StringUtils.isNotEmpty(sysConfig.getConfigValue())) {
//            JSONObject ndCateInterfaceParams = JSON.parseObject(sysConfig.getConfigValue());
//            return ndGetTicketData(task, fileVo, isGetContent, ndCateInterfaceParams);
//        }
        // nc识别
//        else if()
        // 获取TickData
        return ncGetTicketData(task, new ArrayList<FileVo>(){{add(fileVo);}}, isGetContent);
    }
    /**
     * 采用nimble Document 识别
     *
     * @param task
     * @param fileVo
     * @param isGetContent
     * @param ndCateInterfaceParams
     * @return
     * @throws IOException
     */
    private List<TickData> ndGetTicketData(AjTicketTask task, FileVo fileVo, boolean isGetContent, JSONObject ndCateInterfaceParams) throws IOException {
        JSONObject jsonObject = getJsonObject(fileVo, ndCateInterfaceParams, task);
        /*OCRData ocrData = getOcrData(task, fileVo);
        return getTickData(task, isGetContent, fileVo, ocrData.getCostTime(), new HashSet<AjAttachType>() {{
            add(new AjAttachType() {{
                setType(jsonObject.getString("choice"));
            }});
        }}, null, ocrData.getText(), ocrData.getBoxes());*/
        return null;
    }

    /**
     * 采用nimble Document 识别
     *
     * @param task
     * @param lsFileVo
     * @param isGetContent
     * @return
     * @throws IOException
     */
    private List<TickData> ncGetTicketData(AjTicketTask task, List<FileVo> lsFileVo, boolean isGetContent) throws IOException {
        List<Map<String,String>> ncFileMap = getNcData( lsFileVo,task);
        List<TickData> lsTickData = new ArrayList<>();
        for (Map<String,String> fileMap : ncFileMap) {
            OCRData ocrData = getOcrData(task, fileMap);
            lsTickData.addAll(getTickData(task, isGetContent, fileMap, ocrData.getCostTime(), new HashSet<AjAttachType>() {{
                add(new AjAttachType() {{
                    setType(fileMap.get("fileType"));
                }});
            }}, null, ocrData.getText(), ocrData.getBoxes()));
        }
        return lsTickData;
    }
    /**
     * 采用OCR识别
     *
     * @param task
     * @param fileMap
     * @param isGetContent
     * @return
     * @throws IOException
     */
    public List<TickData> ncGetTicketData2(AjTicketTask task, Map<String,String> fileMap, boolean isGetContent) throws IOException {
        OCRData ocrData = getOcrData(task, fileMap);
        return getTickData(task, isGetContent, fileMap, ocrData.getCostTime(), new HashSet<AjAttachType>() {{
            add(new AjAttachType() {{
                setType(fileMap.get("source_name"));
            }});
        }}, null, ocrData.getText(), ocrData.getBoxes());
    }

    private OCRData getOcrData(AjTicketTask task, Map<String,String> fileMap) throws IOException {
        String value = fileMap.get("base64");
        initConfig();
        // ocr识别不需要token
        /*if (StringUtils.isEmpty(token)) {
            setToken();
        }*/

        Date startTime = new Date();
        OkHttpClient.Builder builder = new OkHttpClient.Builder();

        OkHttpClient client = builder.readTimeout(10, TimeUnit.MINUTES)
                .build();
        // 20250425update
        Map<String,Object> assetParam = new HashMap<>();
        assetParam.put("type","file");
        assetParam.put("name",fileMap.get("fileName"));
        assetParam.put("base64",value);
        RequestBody body = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"),
                /*JSON.toJSONString(new HashMap<String, Object>() {{
                    put("data_file", value);
                }}));*/
                JSON.toJSONString(new HashMap<String, Object>() {{
                    put("asset", assetParam);
                }}));
        // 创建请求

        Request.Builder requestBuilder = new Request.Builder()
                .url(fanhanAIOCRURL)
                .post(body);

        if (StringUtils.isNotEmpty(token)) {
            requestBuilder.addHeader("session", token);
        }
        Request request = requestBuilder.build();
        Response response = client.newCall(request).execute();
        Long costTime = new Date().getTime() - startTime.getTime();
        // 检查响应码
        if (!response.isSuccessful()) {
            countUtils.countApi("call_ocr", StringUtils.substring(response.toString(), 0, 1000), 2, "fileVo.getApiUrl()", task, null);
            throw new IOException("Unexpected code " + response);
        }
        String bodyStr = response.body().string();
        // 打印响应体
//        log.info("获取文字识别 >> {}", bodyStr);
        JSONObject jsonObject = JSON.parseObject(bodyStr);
        countUtils.countApi("call_ocr", StringUtils.substring(bodyStr, 0, 1000), 1, "fileVo.getApiUrl()", task, null);
//        JSONObject detail = jsonObject.getJSONObject("detail");
//        if(detail.getInteger("code") != 200){
//            throw new OCRException("OCR 文字识别异常：" + detail.getString("message"));
//        }
//        JSONArray ocrResults = detail.getJSONObject("content").getJSONArray("data");
        // 20250425update
        //JSONArray ocrResults = jsonObject.getJSONArray("data");
        JSONArray ocrResults = jsonObject.getJSONArray("pages");

        JSONArray text = new JSONArray();
        JSONArray boxes = new JSONArray();
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < ocrResults.size(); i++) {

            JSONObject map = ocrResults.getJSONObject(i);
            List<String> text2 = map.getList("texts", String.class);
            boxes.add(map.getJSONArray("boxes"));
            text.add(map.getJSONArray("texts"));
            String texts = String.join("", text2);

            content.append(texts);
        }
        log.info(" url: {}, 文字识别结果：{},", "fileVo.getSourceUrl()", content);
        return new OCRData() {{
            setBoxes(boxes);
            setText(text);
            setContent(content.toString());
            setCostTime(costTime);
        }};
    }

    @Nullable
    private List<TickData> ocrGetTicketData(AjTicketTask task, FileVo fileVo, boolean isGetContent) throws IOException {
        /*OCRData ocrData = getOcrData(task, fileVo);
        String content = ocrData.getContent();
        List<AjAttachType> attachTypes = attachTypeMapper.selectAttachTypeList(new AjAttachType() {{
            setTicketType(task.getTicketType());
        }});
        Set<AjAttachType> matchedAttachTypes = new HashSet<>();
        JSONObject originalData = new JSONObject();

        for (AjAttachType attachType : attachTypes) {
            if (StringUtils.isEmpty(attachType.getCondition())) {
                log.error("正则为空，忽略此规则。");
                continue;
            }
            Pattern pattern = null;
            try {
                pattern = Pattern.compile(attachType.getCondition());

            } catch (PatternSyntaxException e) {
                log.error("正则表达式出错，{}", attachType.getCondition(), e);
                continue;
            }
            Matcher matcher = pattern.matcher(content);
            if (matcher.find()) {
                matchedAttachTypes.add(attachType);
                log.info("检测附件类型为 -> {}, 模版名称为 -> {}", attachType.getType(), attachType.getName());
                if (attachType.getSort() >= 0) {
                    break;
                }
            }
        }

        return getTickData(task, isGetContent, fileVo, ocrData.getCostTime(), matchedAttachTypes, originalData, ocrData.getText(), ocrData.getBoxes());*/
        return null;
    }

    @Nullable
    private List<TickData> getTickData(AjTicketTask task, boolean isGetContent,Map<String,String> fileMap, Long cost_time, Set<AjAttachType> matchedAttachTypes, JSONObject originalData, JSONArray text, JSONArray boxes) {
        List<TickData> tickDataList = new ArrayList<>();
        if (!isGetContent) {
            return new ArrayList<TickData>() {{
                add(
                        new TickData() {{
                            setBoxes(boxes);
                            setTexts(text);
                            setCostOcr(cost_time);
                        }}
                );
            }};
        } else if (matchedAttachTypes.size()>0) {
            for (AjAttachType attachType : matchedAttachTypes) {
                try {
                    Date start = new Date();
                    /*AjDatasource nimbleDatasource = getNimbleDocumentSource(task, attachType);
                    if (nimbleDatasource == null) {
                        log.info("{}附件分类，大模型识别中。。。", attachType.getType());
                        String fields = fieldHelper.getFields(attachType.getType());
//            log.info("取出附件类型的字段：{}", fields);


                        if (StringUtils.isNotEmpty(fields)) {
                            originalData = getContent(fileVo, fields, task);
                        }
                        if (originalData == null) {
                            originalData = new JSONObject();
                        }
                    } else {
                        // 走nimbleDocument 分支
                        log.info("{}附件分类，nimble document，识别中。。。", attachType.getType());
                        originalData = getNimbleDocumentContent(nimbleDatasource, fileVo, task);
                    }*/
                    // 直接取NC返回的data
                    originalData = JSON.parseObject(fileMap.get("data"));
                    Long costAi = new Date().getTime() - start.getTime();
                    JSONObject finalOriginalData = originalData;
                    tickDataList.add(new TickData() {{
                        setTickCate(attachType.getType());
                        setTickName(attachType.getName());
                        setBoxes(boxes);
                        setTexts(text);
                        setCostOcr(cost_time);
                        setCostAi(costAi);
                        setOriginalData(finalOriginalData);
                    }});
                } catch (Exception e) {
                    log.error("ocr识别出错", e);

                }
            }
            return tickDataList;
        }
        return null;
    }

    private JSONObject getNimbleDocumentContent(AjDatasource nimbleDatasource, FileVo fileVo, AjTicketTask task) throws IOException {
        if (StringUtils.isEmpty(nimbleDatasource.getParams())) {
            log.error("nimble datasource params is empty");
            return null;
        }
        JSONObject params = JSON.parseObject(nimbleDatasource.getParams());
        return getJsonObject(fileVo, params, task);
    }

    private JSONObject getJsonObject(FileVo fileVo, JSONObject params, AjTicketTask task) throws IOException {
        params.put("input_base64_string", fileVo.parseBase64Url());
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        JSONObject jsonrpc = new JSONObject() {{
            put("jsonrpc", "2.0");
            put("method", "/document/extract/call");

            put("params", params);
        }};
        OkHttpClient client = builder.readTimeout(contentTimeoutMinutes, TimeUnit.MINUTES)
                .build();
        RequestBody body = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"),
                JSON.toJSONString(jsonrpc));
        // 创建请求
        Request.Builder requestBuilder = new Request.Builder()
                .url(ndUrl)
                .post(body);
        if (StringUtils.isNotEmpty(ndToken)) {
            requestBuilder.addHeader("session", ndToken);
        }
        Request request = requestBuilder.build();

        Response response = client.newCall(request).execute();

        // 检查响应码
        if (!response.isSuccessful()) {
            countUtils.countApi("call_model", StringUtils.substring(response.toString(), 0, 1000), 2, fileVo.getApiUrl(), task, null);
            throw new IOException("Unexpected code " + response);
        }
        String bodyStr = response.body().string();
        log.info("获取nimble_document文字提取：url: {} , 响应： {}", ndUrl, bodyStr);
        countUtils.countApi("call_model", StringUtils.substring(bodyStr, 0, 1000), 1, fileVo.getApiUrl(), task,null);
        JSONObject jsonObject = JSON.parseObject(bodyStr);
        return jsonObject.getJSONObject("result").getJSONObject("json_data");
    }


    public AjDatasource getNimbleDocumentSource(AjTicketTask task, AjAttachType attachType) throws AjObjectNotFoundException {
        AjObject object;
        try {
            object = commonHandler.getObject(task.getTicketType(), attachType.getType(), null);
        } catch (
                Exception e) {
            log.error("获取nimble datasource出错", e);

            return null;
        }
        if (object == null) {
            return null;
        }
        log.info("nimble datasource object:{}", JSON.toJSONString(object));
        if (!object.getType().equals("Datasource")) {
            return null;
        }
        if (StringUtils.isNull(object.getSourceId())) {
            return null;
        }
        AjDatasource dataSource = datasourceMapper.selectDatasourceById(object.getSourceId());
        log.info("nimble datasource:{}", JSON.toJSONString(dataSource));
        if (!dataSource.getType().equals("nd")) {
            return null;
        }
        return dataSource;
    }

    /**
     * @param fileVo
     * @param template
     * @return
     * @throws IOException {
     *                     "detail": {
     *                     "code": 200,
     *                     "content": {
     *                     "data": {
     *                     "detail_words": {
     *                     "合同编号": "C156023091800200",
     *                     "合同单价": "3750 元/吨",
     *                     "抬头": "江苏巴大饲料有限公司与厦门建发物产有限公司",
     *                     "购买方": "江苏巴大饲料有限公司",
     *                     "品名": "200型菜粕",
     *                     "数量": "125.74吨"
     *                     },
     *                     "detail_table": [
     *                     {
     *                     "入库时间": "2023/10/8",
     *                     "过磅车号": "苏盐城货222168",
     *                     "入库净重": "45.46",
     *                     "备注": "江苏巴大借大丰"
     *                     },
     *                     {
     *                     "入库时间": "2023/10/8",
     *                     "过磅车号": "苏盐城货2221168",
     *                     "入库净重": "38.6",
     *                     "备注": "江苏巴大借大丰"
     *                     },
     *                     {
     *                     "入库时间": "2023/10/8",
     *                     "过磅车号": "苏盐城货2221168",
     *                     "入库净重": "41.68",
     *                     "备注": "江苏巴大借大丰"
     *                     },
     *                     {
     *                     "入库时间": "2023/10/10",
     *                     "过磅车号": "豫NC6889",
     *                     "入库净重": "41.46",
     *                     "备注": "江苏巴大还大丰"
     *                     },
     *                     {
     *                     "入库时间": "2023/10/10",
     *                     "过磅车号": "豫NH5009",
     *                     "入库净重": "38.52",
     *                     "备注": "江苏巴大还大丰"
     *                     },
     *                     {
     *                     "入库时间": "2023/10/10",
     *                     "过磅车号": "苏JW1310",
     *                     "入库净重": "39.82",
     *                     "备注": "江苏巴大还大丰"
     *                     }
     *                     ]
     *                     },
     *                     "info": {
     *                     "version": "v0.6.4",
     *                     "time": {
     *                     "process": [
     *                     {
     *                     "ocrbase_fn": {
     *                     "TextTR": 1.191222906112671,
     *                     "WordTR": 1.057579517364502,
     *                     "all": 2.2488226890563965
     *                     },
     *                     "table_fn": {
     *                     "CatelogFilter": 0.003499269485473633,
     *                     "HeaderFooterFilter": 0.0028924942016601562,
     *                     "OCRTableExtractor": 0.43530917167663574,
     *                     "all": 0.4500885009765625
     *                     },
     *                     "llm_fn": {
     *                     "all": 5.1987082958221436
     *                     }
     *                     },
     *                     {
     *                     "all": 7.908924341201782
     *                     }
     *                     ],
     *                     "all": 7.909114599227905
     *                     },
     *                     "imageShow": null
     *                     }
     *                     },
     *                     "message": ""
     *                     }
     *                     }
     */
    public JSONObject getContent(FileVo fileVo, String template, AjTicketTask task) throws IOException {

        OkHttpClient.Builder builder = new OkHttpClient.Builder();

        OkHttpClient client = builder.readTimeout(contentTimeoutMinutes, TimeUnit.MINUTES)
                .build();
        Map<String, Object> params = new HashMap<>();
        params.put("format_dict", template);
        log.info("OCR文字提取参数：{}", params);
        params.put("data_file", fileVo.parseBase64Url());
        params.put("needOcrPre", true);
        // 大模型名称
        SysConfig config = sysConfigMapper.checkConfigKeyUnique("MODEL_NAME");
        if (config != null && StringUtils.isNotEmpty(config.getConfigValue())) {
            model_name = config.getConfigValue();
        }
        log.info("大模型名称为：{}", model_name);
        params.put("model_name", model_name);
        RequestBody body = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"),
                JSON.toJSONString(params));
        // 创建请求
        Request.Builder requestBuilder = new Request.Builder()
                .url(fanhanAIContractTableURL)
                .post(body);
        if (StringUtils.isNotEmpty(token)) {
            requestBuilder.addHeader("session", token);
        }
        Request request = requestBuilder.build();

        Response response = client.newCall(request).execute();
        // 检查响应码
        if (!response.isSuccessful()) {
            countUtils.countApi("call_model", StringUtils.substring(response.toString(), 0, 1000), 2, fileVo.getApiUrl(), task, null);
            throw new IOException("Unexpected code " + response);
        }
        String bodyStr = response.body().string();
        log.info("获取OCR文字提取：{}", bodyStr);
        JSONObject jsonObject = JSON.parseObject(bodyStr);
        countUtils.countApi("call_model", StringUtils.substring(bodyStr, 0, 1000), 1, fileVo.getApiUrl(), task, null);
//        JSONObject detail = jsonObject.getJSONObject("detail");
//        if(detail.getInteger("code") != 200){
//            throw new OCRException("OCR 文字提取异常：" + detail.getString("message"));
//        }
//        return detail.getJSONObject("content").getJSONObject("data");
        return jsonObject.getJSONObject("data");

    }

    public JSONObject getStamp(FileVo fileVo, AjTicketTask task) throws IOException {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();

        OkHttpClient client = builder.readTimeout(2, TimeUnit.MINUTES)
                .build();
        Map<String, Object> params = new HashMap<>();
        params.put("data_file", fileVo.parseBase64Url());
        RequestBody body = RequestBody.create(
                MediaType.parse("application/json; charset=utf-8"),
                JSON.toJSONString(params));
        // 创建请求
        Request request = new Request.Builder()
                .url(fanhanAIStampURL)
                .addHeader("session", token)
                .post(body)
                .build();
        Response response = client.newCall(request).execute();
        // 检查响应码
        if (!response.isSuccessful()) {
            countUtils.countApi("call_stamp", StringUtils.substring(response.toString(), 0, 1000), 2, fileVo.getApiUrl(), task, null);
            throw new IOException("Unexpected code " + response);
        }
        String bodyStr = response.body().string();
        log.info("获取印章：{}", bodyStr);
        countUtils.countApi("call_stamp", StringUtils.substring(bodyStr, 0, 1000), 1, fileVo.getApiUrl(), task, null);
        JSONObject jsonObject = JSON.parseObject(bodyStr);
//        JSONObject detail = jsonObject.getJSONObject("detail");
//        if(detail.getInteger("code") != 200){
//            throw new OCRException("OCR 文字提取异常：" + detail.getString("message"));
//        }
//        return detail.getJSONObject("content").getJSONObject("data");
        return jsonObject.getJSONObject("data");
    }
    /**
     * NC接口调用返回附件列表
     * @param fileVoList
     * @param task
     * @return
     * @throws IOException
     */
    public List<Map<String,String>> getNcData(List<FileVo> fileVoList, AjTicketTask task) throws IOException {
        if (ncUrl == null) initConfig();
        // 添加不走import逻辑
        int ncImportId=0;
        if(task.getRemark()!=null && task.getRemark().equals("1")){
            if(task.getCallbackStatus()==null || task.getCallbackStatus()==0){
                throw new IOException("Nc improt 返回Id为空 ");
            }
            ncImportId = task.getCallbackStatus();
        }else {
            JSONObject ncData = ncImport(fileVoList, task);
            ncImportId =ncData.getJSONObject("data").getJSONObject("record").getInteger("id");
            // 20250428 添加回调id
            //JSONArray urlArray =ncData.getJSONObject("data").getJSONArray("receipts");
            attachResultMapper.execUpdateSql("update aj_ticket_task set callback_status="+ncImportId+ " where id="+task.getId());

            try {
                Thread.sleep(ncSleep * 1000);
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        JSONObject ncExportData = ncExport(fileVoList, task, ncImportId);
        JSONObject result = new JSONObject();
        JSONArray receipts = ncExportData.getJSONObject("data").getJSONArray("receipts");
        List<Map<String,String>> filesList = new ArrayList<>();
        for (int i = 0; i < receipts.size(); i++) {
            JSONObject receipt = receipts.getJSONObject(i);
            Map<String,String> fileMap = new HashMap<>();
            /*if(receipt.containsKey("data")){
                JSONObject data =  receipt.getJSONObject("data");
                JSONObject origin = data.getJSONObject("origin");

                JSONObject originFile = origin.getJSONArray("files").getJSONObject(0);
                FileVo fileVo = FileUtils.toFileVo(originFile);
                JSONArray files = data.getJSONArray("files");
                if(files != null && files.size() > 1){
                    fileVo.setIsSplit(true);
                }
                for(int j = 0; j < files.size(); j++){
                    JSONObject file = files.getJSONObject(j);
                    FileVo fileVo1 = FileUtils.toFileVo(file);
                    filesList.add(fileVo1);
                }
                fileVo.setChildren(filesList);
                data.put("fileVo", fileVo);
            }*/


            JSONObject originObj = receipt.getJSONObject("origin");
            String originId = originObj.getString("id");
            JSONObject tmpObj = originObj.getJSONObject("meta");
            String sourceUrl = "";
            if(tmpObj!=null){
                sourceUrl = tmpObj.getString("url");
            }
            // 获取到分隔文件base64
            JSONArray files = receipt.getJSONArray("files");
            if (files.size()>0) {
                JSONObject file = files.getJSONObject(0);
                fileMap.put("base64", file.getString("base64"));
                fileMap.put("fileType", file.getString("mimeType"));
                fileMap.put("fileName", file.getString("uuid")+file.getString("name"));
                fileMap.put("originId",originId);
                fileMap.put("sourceUrl",sourceUrl);
                // data赋值
                fileMap.put("data",receipt.getString("data"));
                // source_name
                fileMap.put("source_name",receipt.getJSONObject("source").getString("name"));
                filesList.add(fileMap);
            }

        }
        return filesList;
    }

    public JSONObject ncImport(List<FileVo> fileVoList, AjTicketTask task) throws IOException {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();

        OkHttpClient client = builder.readTimeout(20, TimeUnit.MINUTES).build();

        MediaType mediaType = MediaType.parse("application/json");
        JSONObject params = new JSONObject();
        JSONObject object = new JSONObject();
        // 请求参数场景ID
        AjObject obj = ajObjectMapper.selectObjectByName(task.getTicketType(),task.getTicketType());
        if(obj!=null && obj.getNcScenarioid()!=null && obj.getNcScenarioid().length()>0){
            ncScenarioId = obj.getNcScenarioid();
        }
        params.put("scenarioId", Integer.parseInt(ncScenarioId));
        //object.put("id", task.getId());
        /*JSONArray files = new JSONArray();
        for(FileVo f:fileVoList){
            Map<String,String> map = new HashMap<>();
            map.put("type","file");
            map.put("name",f.getFileName());
            map.put("base64",f.parseBase64Url());
            files.add(map);
        }*/
        JSONArray filesArray = new JSONArray();
        // 原单数据添加
        JSONObject fObjReort = new JSONObject();
        fObjReort.put("name",task.getTicketType());
        JSONObject info = JSONObject.parseObject(task.getInfo());
        JSONObject fObjReortHead = info.getJSONObject("Basic");
        fObjReortHead.putAll(info.getJSONObject("Detail"));
        fObjReort.put("data", fObjReortHead);
        filesArray.add(fObjReort);

        for(FileVo f:fileVoList){
            JSONArray files = new JSONArray();
            JSONObject fObj = new JSONObject();
            Map<String,String> map = new HashMap<>();
            map.put("type","file");
            map.put("name",f.getFileName());
            map.put("base64",f.parseBase64Url());
            files.add(map);
            fObj.put("id", task.getId());
            fObj.put("assets", files);
            Map<String,String> mapUrl = new HashMap<>();
            mapUrl.put("url",f.getSourceUrl());
            fObj.put("meta", mapUrl);
            filesArray.add(fObj);
        }
        params.put("receiptObjects", filesArray);
        RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(params));
        Request request = new Request.Builder()
                .url(ncUrl + "/api/server/nc/record/import")
                .post(body)
                .addHeader("secret", ncSecret)
                .addHeader("Accept", "*/*")
                .addHeader("Content-Type", "application/json;charset=UTF-8")
                .build();

        Response response = client.newCall(request).execute();
        String bodyStr = response.body().string();
        JSONObject bodyJson = JSONObject.parseObject(bodyStr);
        if (response.code()!=201 || bodyJson.getInteger("code") != 200) {
            countUtils.countApi("nc_import", StringUtils.substring(response.toString(), 0, 1000), 2, ncUrl, task, null);
            String errorMsg = StringUtils.format("Unexpected code :\n Request: {}  \n Response: {}", request, bodyJson);
            throw new IOException(errorMsg);
        }
        countUtils.countApi("nc_import", StringUtils.substring(bodyStr, 0, 1000), 1, ncUrl, task, null);
        return bodyJson;
    }

    public JSONObject ncExport(List<FileVo> fileVoList, AjTicketTask task, Integer id) throws IOException {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();

        OkHttpClient client = builder.readTimeout(20, TimeUnit.MINUTES).build();

        MediaType mediaType = MediaType.parse("application/json;charset=UTF-8");
        JSONObject params = new JSONObject();
        params.put("id", id);
        RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(params));
        Request request = new Request.Builder()
                .url(ncUrl + "/api/server/nc/record/export")
                .post(body)
                .addHeader("secret", ncSecret)
                .addHeader("Accept", "*/*")
                .addHeader("Content-Type", "application/json")
                .build();

        Response response = client.newCall(request).execute();
        String bodyStr = response.body().string();
        JSONObject bodyJson = JSONObject.parseObject(bodyStr);
        if (response.code()!=201 || bodyJson.getInteger("code") != 200) {
            countUtils.countApi("nc_export", StringUtils.substring(response.toString(), 0, 1000), 2, ncUrl, task, null);
            throw new IOException("Unexpected code " + response);
        }
        countUtils.countApi("nc_export", StringUtils.substring(bodyStr, 0, 1000), 1, ncUrl, task, null);
        return bodyJson;
    }
}
